// -----------------------------------------------------------------
//     Learning Team C
//       Members:
//       Jon Mann
//       Jared Pack
//       Joshua Clayton
//     CS 4280  9:30 am
//     Team Assignment #4
//     Instructor: Mr. Rague
//     Due: 03/04/10
//     Version: 1.0
// -----------------------------------------------------------------
// This program builds a 3D scene 
// that uses the keyboard and mouse for interaction.
// -----------------------------------------------------------------


// -----------------------------------------------------------------
//   Change Control Section
// -----------------------------------------------------------------
//       Team Member:  Jon Mann
//       Version: 1.0
//       Date:  02/24/2010
//       Built a rough first run for program, 
//			borrowing heavily from the code in the book
//		 Included a table and a starter robot head (the teapot)
//		 Set up keyboard functions for model switching (wire frame to solid)
//			and visible/invisible switch
//		 Set up mouse function to rotate robot "head"
//
//       Team Member:  Jon Mann
//       Version: 1.1
//       Date:  02/26/2010
//       Added myIdle() to handle glutPostRedisplay()
//
//       Team Member:  Jon Mann
//       Version: 1.2
//       Date:  02/27/2010
//       Added a robot body
//		 Updated wireframe/solid code
//		 Updated visible/invisible code
//		 Replaced teapot head with new head built from spheres and cones
//
//       Team Member:  Jon Mann
//       Version: 1.2
//       Date:  03/01/2010
//       Added code to move point light with mouse
//		 Moved some rotational code out of mouseMove() function for modularity
//
//       Team Member:  Jon Mann
//       Version: 1.3
//       Date:  03/02/2010
//       Changed from orthographic view to perspective view
//		 Cleaned up some viewing problems
//		 Added rotation code to move the camera with the keyboard
//		 Added calcZ() function to keep the camera a specific 
//			distance from the center of scene (orbit)
//
//       Team Member:  Jared Pack
//       Version: 1.4
//       Date:  03/04/2010
//       Changed the boolean that switches from solid to wireframe so that there
//		 are individual booleans, one for the robot and one for the table.
// -----------------------------------------------------------------

#ifdef WIN32
#include <windows.h>
#endif

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include <math.h>
#include <iostream>

#include "reshape.h"

using namespace std;

// built mostly from the code on page 253

static void tableBuilder(double topWid, double topThick, double legThick, double legLen);
static void robotBuilder();

// these hold our mouse coordinates
extern int mousePosX;
extern int mousePosY;

// these adjust the camera position
extern int camX;
extern int camY;
extern double dist;
extern double zDist;

// these help toggle object settings in the scene
extern bool solidRobot;
extern bool solidTable;
extern bool tableVisible;
extern bool robotVisible;

void robotHead(void)
{
        float rotateMatrix[16] = {1.0, 0.0, 0.0, 0.0,
                                   0.0, 1.0, 0.0, 0.0,
                                   0.0, 0.0, 1.0, 0.0,
                                   0.0, 0.0, 0.0, 1.0};
	glPushMatrix(); // pushing and popping our matrix isolates our transformations

	glTranslated(world_win_width * 0.75f, world_win_height * 0.79f, 0.0);

        float mousePosZ = 500;
        float normalize = sqrt(mousePosX * mousePosX +
                               mousePosY * mousePosY +
                               mousePosZ * mousePosZ);

        rotateMatrix[0] = mousePosZ;
        rotateMatrix[1] = 0;
        rotateMatrix[2] = mousePosX;
        rotateMatrix[4] = 0;
        rotateMatrix[5] = mousePosY;
        rotateMatrix[6] = -mousePosZ;
        rotateMatrix[8] = -mousePosX;
        rotateMatrix[9] = mousePosZ;
        rotateMatrix[10] = mousePosY + mousePosZ;
        rotateMatrix[15] = 2;

    //    glMultMatrixf(rotateMatrix);
//	glRotated(-90, 0, 1, 0);
//	glRotated((mousePosX + world_win_width * 2.0f) * (-.2f), 0, -1, 0);
//	glRotated(-90, 0, 0, 1);
//	glRotated((mousePosY * 1.5f) * .2f, 0, 0, 1);

	// build the head
	glPushMatrix();
	glScaled(50, 50, 50);
	if (solidRobot)
	{
		glutSolidSphere(1, 10, 10);
	}
	else
	{
		glutWireSphere(1, 10, 10);
	}
        glLoadIdentity();
        glBegin(GL_LINES);
            glVertex3f(0, 0, 0);
            glVertex3f(mousePosX, mousePosY, mousePosZ);
        glEnd();
	glPopMatrix();

	// build a nose
	glPushMatrix();
	glRotated(90, 0, 1, 0);
	glTranslated(0, 0, 50);
	glScaled(10, 10, 20);
	if (solidRobot)
	{
		glutSolidCone(1, 2, 5, 5);
	}
	else
	{
		glutWireCone(1, 2, 5, 5);
	}
	glPopMatrix();

	// build an eye
	glPushMatrix();
	glRotated(90, 0, 1, 0);
	glTranslated(20, 20, 30);
	glScaled(10, 10, 10);
	if (solidRobot)
	{
		glutSolidSphere(1, 10, 10);
	}
	else
	{
		glutWireSphere(1, 10, 10);
	}
	glPopMatrix();

	// build another eye
	glPushMatrix();
	glRotated(90, 0, 1, 0);
	glTranslated(-20, 20, 30);
	glScaled(10, 10, 10);
	if (solidRobot)
	{
		glutSolidSphere(1, 10, 10);
	}
	else
	{
		glutWireSphere(1, 10, 10);
	}
	glPopMatrix();

	glPopMatrix(); // pushing and popping our matrix isolates our transformations
}

void table (void)
{
	glPushMatrix(); // pushing and popping our matrix isolates our transformations

	glTranslated(world_win_width * 0.25f, world_win_height * 0.25f, 0.0);
	// should we rotate the table as well? 
	// it's difficult to see very well straight on...
	//glRotated(-90, 0, 1, 0);
	//glRotated(mousePosX * .2f, 0, -1, 0);
	//glRotated(-90, 0, 0, 1);
	//glRotated(mousePosY * .2f, 0, 0, 1);

	tableBuilder(150, 10, 10, 150);

	glPopMatrix(); // pushing and popping our matrix isolates our transformations
}

// robot
void robotBody()
{  
	glPushMatrix(); // pushing and popping our matrix isolates our transformations

	glTranslated(world_win_width * 0.75f, world_win_height * 0.70f, 0.0);

	glPushMatrix();  // draw the torso
	glTranslated(0, -100, 0);
	glScaled(100, 200, 100);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	// add one leg
	glPushMatrix();
	glTranslated(-50, -200, 0);
	glScaled(50, 50, 50);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	// add other leg
	glPushMatrix();
	glTranslated(50, -200, 0);
	glScaled(50, 50, 50);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	// add one upper arm
	glPushMatrix();
	glTranslated(75, -25, 0);
	glScaled(100, 30, 30);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	// add other upper arm
	glPushMatrix();
	glTranslated(-75, -25, 0);
	glScaled(100, 30, 30);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	// add lower arm
	glPushMatrix();
	glTranslated(-115, -65, 0);
	glScaled(25, 100, 25);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	// add other lower arm
	glPushMatrix();
	glTranslated(115, -65, 0);
	glScaled(25, 100, 25);
	if (solidRobot)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();

	glPopMatrix(); // from the push at the top of this function
}

// tableLeg
void tableLeg(double thick, double len)
{
	glPushMatrix();
	glTranslated(0, len/2, 0);
	glScaled(thick, len, thick);
	if (solidTable)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();
}

// table
void tableBuilder(double topWid, double topThick, double legThick, double legLen)
{  // draw the table - a top and four legs
	glPushMatrix();  // draw the table top
	glTranslated(0, legLen, 0);
	glScaled(topWid, topThick, topWid);
	if (solidTable)
	{
		glutSolidCube(1.0);
	}
	else
	{
		glutWireCube(1.0);
	}
	glPopMatrix();
	// start adding legs
	double dist = 0.95 * topWid/2.0 - legThick/2.0;
	glPushMatrix();
	glTranslated(dist, 0, dist);
	tableLeg(legThick, legLen);
	glTranslated(0, 0, -2 * dist);
	tableLeg(legThick, legLen);
	glTranslated(-2 * dist, 0, 2 * dist);
	tableLeg(legThick, legLen);
	glTranslated(0, 0, -2 * dist);
	tableLeg(legThick, legLen);
	glPopMatrix();
}

double calcZ(void)
{
	zDist = sqrt((dist * dist) - ((camX * camX) + (camY * camY)));
	if (zDist > 0)
	{
		return zDist;
	}
	else
	{
		return 0;
	}
}

// display
void display(void)
{
	// set properties of the surface material.  see Figure 8.17, page 392 for other materials
	GLfloat mat_ambient[] = {0.7f, 0.7f, 0.7f, 1.0f}; // gray
	GLfloat mat_diffuse[] = {0.6f, 0.6f, 0.6f, 1.0f};
	GLfloat mat_specular[] = {0.6f, 0.6f, 0.6f, 1.0f};
	GLfloat mat_shininess[] = {50.0f};
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

	// set the light source properties
	GLfloat lightIntensity[] = {0.9f, 0.9f, 0.5f, 1.0f}; // gray
	GLfloat light_position[] = {mousePosX, mousePosY, 200.0f, 1.0f};
	glLightfv(GL_LIGHT0, GL_POSITION, light_position); // location of light number 0, out of 8 possible lights
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightIntensity);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt( (world_win_width/2) + camX, world_win_height/2  + camY, calcZ(),   world_win_width/2, world_win_height/2, 0,    0.0, 1.0, 0.0 );
	
	// start drawing
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // clear the screen and depth buffer

	if (robotVisible)
	{
		robotHead();
		robotBody();
	}

	if (tableVisible)
	{
		table();
	}

	glutSwapBuffers(); // used for double buffering
}
